<?php
include_once 'vendor/autoload.php';
include_once 'Eraser/Temp.php';
include_once 'Eraser/Visits.php';

use Metowolf\Meting;
use lifanko\Temp;
use lifanko\Visits;

class Eraser
{
    const VERSION = '3.1.3';
    const PLATFORM = [
        'qq' => 'QQ音乐库',
        'kg' => '酷狗音乐库',
        'wy' => '网易音乐库'
    ];
    const VALID_TIME = 1800;

    private string $_uid;

    private array $_suppose = array('wy' => 'netease', 'qq' => 'tencent', 'kg' => 'kugou', 'kw' => 'kuwo');

    public static int $now;

    public function __construct()
    {
        self::$now = time();
    }

    function get($key, $msg, $check = true)
    {
        if (empty($_GET[$key]) && $check) {
            echo $this->out([-1, $msg]);
            exit();
        } else {
            return htmlspecialchars(trim($_GET[$key]));
        }
    }

    public function init()
    {
        $visits = new Visits("EraserMusic_");
        $visits = $visits->update();

        return [
            'uid' => $this->_uid,
            'list' => $this->get_list(),
            'history' => $this->play_history(),
            'search_param' => $this->search_param(),
            'version' => [
                'Meting' => Meting::VERSION,
                'backend' => self::VERSION,
            ],
            'visits' => $visits,
            'valid_time' => self::VALID_TIME
        ];
    }

    public function play_history()
    {
        $temp = $this->temp_obj('history_play');
        $play = $temp->get();

        if (!is_array($play)) {
            return [];
        }

        $play = array_reverse($play);

        $ret = [];
        $id_buffer = [];
        foreach ($play as $item) {
            $id = $item['id'];
            if (in_array($id, $id_buffer)) {
                continue;
            }

            array_push($id_buffer, $id);
            array_unshift($ret, [
                'id' => $item['id'],
                'album' => $item['a'],
                'name' => $item['n'],
                'author' => $item['u'],
                'url' => $item['r'],
                'lyric' => $item['l'],
                'platform' => $item['p'],
                'platform_text' => self::PLATFORM[$item['p']],
                'timestamp' => date('m-d H:i', $item['t'])
            ]);
        }

        return $ret;
    }

    public function search_param()
    {
        $temp = $this->temp_obj('history_search');
        $history = $temp->get();
        if ($history) {
            $history = array_column($history, 'k');
            $history = array_reverse($history);
            $history = array_unique($history);
            $history = array_values($history);
            $history = array_slice($history, 0, 30);
        } else {
            $history = [];
        }

        // hot search
        $hot = $this->get_hot_search();

        return [
            'platform' => self::PLATFORM,
            'hot' => $hot,
            'history' => $history,
        ];
    }

    private function get_hot_search()
    {
        // update every 6 hours
        $hot_search_result = $this->temp_obj('hot_search_result', true);
        $hot = $hot_search_result->get();

        // using buffer data only if HOT cnt more than 50
        if ($hot && count($hot) > 50) {
            return $hot;
        }

        $temp = $this->temp_obj('hot_search', true);
        $hot = $temp->get();
        if (!$hot) {
            $hot = [];
        }
        $hot = array_reverse($hot);

        $from = strtotime('-7 days');
        $keyword_buffer = '';
        $time_buffer = 0;

        $rank = [];
        $rank_keyword = [];
        foreach ($hot as $item) {
            if ($item['t'] < $from) {
                break;
            }

            // the same keyword has 2s gap
            if ($item['k'] != $keyword_buffer || $time_buffer - $item['t'] >= 2) {
                $keyword_buffer = $item['k'];
                $time_buffer = $item['t'];

                $index = array_search($keyword_buffer, $rank_keyword);
                if ($index === false) {
                    array_push($rank_keyword, $keyword_buffer);
                    array_push($rank, [
                        'k' => $keyword_buffer,
                        't' => $time_buffer,
                        'cnt' => 1
                    ]);
                } else {
                    $rank[$index]['cnt']++;
                }
            }
        }

        $rank_t = array_column($rank, 't');
        $rank_cnt = array_column($rank, 'cnt');

        array_multisort($rank_cnt, SORT_DESC, $rank_t, SORT_DESC, $rank);

        $hot = array_column($rank, 'k');
        $hot = array_slice($hot, 0, 50);

        // buffer for 6 hours
        $hot_search_result->save($hot, 3600 * 6);

        return $hot;
    }

    public function wipe($key)
    {
        $temp = $this->temp_obj($key);
        return $temp->del();
    }

    public function search($platform, $keyword)
    {
        $keyword = trim($keyword);

        $temp = $this->temp_obj('history_search');
        $history = $temp->get();
        $history = $history ? $history : [];
        array_push($history, [
            'p' => $platform,
            'k' => $keyword,
            't' => self::$now
        ]);
        $temp->save($history, MAX_TEMP);

        $temp = $this->temp_obj('hot_search', true);
        $hot = $temp->get();
        $hot = $hot ? $hot : [];
        array_push($hot, [
            'p' => $platform,
            'k' => $keyword,
            't' => self::$now
        ]);
        $temp->save($hot, MAX_TEMP * 2);

        $api = new Meting($this->_suppose[$platform]);

        $res = $api->format(true)->search($keyword, [
            'page' => 1,
            'limit' => 20
        ]);
        $res = json_decode($res, true);

        return $res;
    }

    public function save_list($playlist)
    {
        $temp = $this->temp_obj('playlist');
        $temp->save($playlist, MAX_TEMP);
    }

    public function get_list()
    {
        $temp = $this->temp_obj('playlist');
        $playlist = $temp->get();

        if (empty($playlist)) {
            return [];
        } else {
            if ($playlist[0]['load_at'] < self::$now - self::VALID_TIME) {
                // reload song

                $data = $this->get_data(
                    $playlist[0]['platform'],
                    $playlist[0]['url_id'],
                    $playlist[0]['lyric_id'],
                    $playlist[0]['album'],
                    $playlist[0]['name'],
                    $playlist[0]['author'],
                    $playlist[0]['id'],
                );

                $playlist[0]['url'] = $data['url'];
                $playlist[0]['lyric'] = $data['lyric'];
                $playlist[0]['load_at'] = self::$now;

                // update playlist
                $this->save_list($playlist);
            }
            return $playlist;
        }
    }

    private function temp_obj($key, $global = false)
    {
        if ($global) {
            $temp = new Temp($key . '-GLOBAL');
        } else {
            $temp = new Temp($key . '-' . $this->_uid);
        }
        $temp->setPrefix('Eraser_Music-');

        return $temp;
    }

    public function get_data($platform, $url_id, $lyric_id, $album, $name, $author, $song_id)
    {
        $api = new Meting($this->_suppose[$platform]);

        $url = $api->format(true)->url($url_id);
        $lyric = $api->format(true)->lyric($lyric_id);

        $url = json_decode($url, true)['url'];
        if (strlen($url) > 3) {
            $temp = $this->temp_obj('history_play');
            $play = $temp->get();
            $play = $play ? $play : [];
            array_push($play, [
                'id' => $song_id,
                'p' => $platform,
                'a' => $album,
                'n' => $name,
                'u' => $author,
                'r' => $url_id,
                'l' => $lyric_id,
                't' => self::$now
            ]);
            $temp->save($play, MAX_TEMP);
        } else {
            $url = 'FAIL';
        }

        return [
            'url' => $url,
            'lyric' => json_decode($lyric, true)['lyric']
        ];
    }

    public function get_album($platform, $album_id)
    {
        $api = new Meting($this->_suppose[$platform]);

        $album = $api->format(true)->pic($album_id, 800);

        return json_decode($album, true)['url'];
    }

    public function out($param)
    {
        return json_encode([
            'code' => $param[0],
            'msg' => $param[1],
            'data' => empty($param[2]) ? null : $param[2],
            'timestamp' => self::$now
        ]);
    }

    /**
     * @param string $uid
     */
    public function setUid(string $uid): void
    {
        if (empty($uid)) {
            $this->out([0, 'Unique Label Error']);
            exit();
        }
        $this->_uid = $uid;
    }
}

